/*
 * Codegen.cpp
 *
 *  Created on: 1 Nov 2010
 *      Author: kn308
 */

#include <sstream>
#include <iomanip>

#include <MAlice/MAlice.h>
#include <MAlice/Codegen.h>
#include <MAlice/MAliceDriver.hpp>
#include <MAlice/Ast/StringLiteralAst.h>
#include <MAlice/Ast/IntegerLiteralAst.h>
#include <MAlice/Ast/CharacterLiteralAst.h>

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/PassManager.h>
#include <llvm/Instructions.h>
#include <llvm/Pass.h>
#include <llvm/Function.h>

#include <llvm/Support/raw_ostream.h>

#include <llvm/Analysis/Verifier.h>

#include <llvm/CodeGen/DwarfWriter.h>

#include <llvm/Bitcode/Archive.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Bitcode/BitstreamReader.h>
#include <llvm/Bitcode/BitstreamWriter.h>

#include <llvm/Target/TargetData.h>
#include <llvm/Target/TargetSelect.h>

#include <llvm/Transforms/Scalar.h>
#include <llvm/Target/TargetSelect.h>
#include <llvm/TypeSymbolTable.h>
#include <llvm/Module.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Passes.h>
#include <llvm/Analysis/LoopPass.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/System/DynamicLibrary.h>
#include <llvm/Target/TargetData.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Support/PassNameParser.h>
#include <llvm/Support/PluginLoader.h>

#include <llvm/Module.h>
#include <llvm/PassManager.h>
#include <llvm/CallGraphSCCPass.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Analysis/DebugInfo.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Analysis/LoopPass.h>
#include <llvm/Analysis/CallGraph.h>
#include <llvm/Target/TargetData.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Support/PassNameParser.h>
#include <llvm/Support/Debug.h>
#include <llvm/Support/IRReader.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/PluginLoader.h>
#include <llvm/Support/PrettyStackTrace.h>
#include <llvm/Support/StandardPasses.h>
#include <llvm/Support/SystemUtils.h>
#include <llvm/LinkAllPasses.h>
#include <llvm/LinkAllVMCore.h>


#include <llvm/Support/IRBuilder.h>


#include <MAlice/Ast/TypeAst.h>
#include <MAlice/Ast/PointerTypeAst.h>

#include <MAlice/Ast/ListAst.h>
#include <MAlice/Ast/FunctionProtoAst.h>
#include <MAlice/Ast/FunctionParameterAst.h>
#include <MAlice/Ast/FunctionDeclAst.h>

static MAlice::Codegen* gCodegen = NULL;

static location mLocation;

namespace MAlice
{

/* auto reverse class */
AutoReverse::AutoReverse(Codegen* g) :
	mG(g)
{
	mBB = mG->getBuilder()->GetInsertBlock();
	mG->pushScope();
}

AutoReverse::~AutoReverse()
{
	mG->popScope();

	// if there is not basic block prior to this auto reverse, there is nothing to be popped!
	if(!mBB) return;
	mG->getBuilder()->SetInsertPoint(mBB);
}


static const int gEntryBlockFinalizerPid = 0;
class EntryBlockFinalizer : public llvm::FunctionPass
{
public:
	EntryBlockFinalizer()
	 : FunctionPass(&gEntryBlockFinalizerPid)
	{
	}

	virtual const char* getPassName() const {
		return "EntryBlockFinalizer";
	}

	// this is our level 2 semantical analyser on instruction level
    virtual bool runOnFunction(Function &F) {

    	MALICE_TRACE;

    	Function::BasicBlockListType& blocks = F.getBasicBlockList();

    	BasicBlock* entry = &blocks.front();
    	BasicBlock* body  = blocks.getNext(entry);

    	Codegen* g = Codegen::get();
    	Builder* builder = g->getBuilder();

    	if (!entry)
    	{
    		cerr << "Warning: Function '" << F.getNameStr() << "' doesn't have an .entry basic block!" << endl;
    		return false;
    	}
    	else if(!entry->empty() && isa<BranchInst>(entry->back()))
    	{
    		return false;
    	}

    	builder->SetInsertPoint(entry);
    	builder->CreateBr(body);

    	return true;
    }
};

static const int gMAliceFinalizerPid = 0;
class MAliceFinalizer : public llvm::FunctionPass
{
public:
	MAliceFinalizer()
	 : FunctionPass(&gMAliceFinalizerPid)
	{
	}

	virtual const char* getPassName() const {
		return "MAliceFinalizer";
	}

	// this is our level 2 semantical analyser on instruction level
    virtual bool runOnFunction(Function &F) {
    	bool changed = false;
    	return changed;
    }
};





/* codegen class */
Codegen::Codegen() :
	mContext(), mVars(), mEntry(NULL)
{
	MALICE_TRACE;

	gCodegen = this;
	mModule = new Module("MAlice", mContext);
	mBuilder = new Builder(mContext);

	// set target machine instructions
	getModule()->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
	getModule()->setTargetTriple("x86_64-linux-gnu");

	// initialise the data types
	getModule()->addTypeName("number", Type::getInt32Ty(getContext()));
	getModule()->addTypeName("letter", Type::getInt8Ty(getContext()));
	getModule()->addTypeName("sentence", Type::getInt8PtrTy(getContext()));

	// initialise the CRT functions
	{ // printf and scanf
		std::vector<const Type*> args;
		args.push_back(Type::getInt8PtrTy(getContext()));

		FunctionType *FT = FunctionType::get(Type::getInt32Ty(getContext()), args, true);

		//create an external function with no body
		Function *printf = Function::Create(FT, GlobalValue::ExternalLinkage, "printf", getModule());
		printf->setCallingConv(CallingConv::C);

		Function *scanf = Function::Create(FT, GlobalValue::ExternalLinkage, "scanf", getModule());
		scanf ->setCallingConv(CallingConv::C);

		args.clear();
		FT = FunctionType::get(Type::getVoidTy(getContext()), args, false);
		Function *abort = Function::Create(FT, GlobalValue::ExternalLinkage, "abort", getModule());
		abort ->setCallingConv(CallingConv::C);
	}

	{// initialise the constant global variables
		GlobalVariable* fmt;

		fmt = new GlobalVariable(/*Module=*/*getModule(),
		/*Type=*/ArrayType::get(Type::getInt8Ty(getContext()), 3),
		/*isConstant=*/true,
		/*Linkage=*/GlobalValue::PrivateLinkage,
				/*Initializer=*/reinterpret_cast<Constant*> (StringLiteralAst(location(), "%d").generate()),
				/*Name=*/".fmt_d");
		fmt->setAlignment(1);

		fmt = new GlobalVariable(/*Module=*/*getModule(),
		/*Type=*/ArrayType::get(Type::getInt8Ty(getContext()), 3),
		/*isConstant=*/true,
		/*Linkage=*/GlobalValue::PrivateLinkage,
				/*Initializer=*/reinterpret_cast<Constant*> (StringLiteralAst(location(), "%s").generate()),
				/*Name=*/".fmt_s");
		fmt->setAlignment(1);

		fmt = new GlobalVariable(/*Module=*/*getModule(),
		/*Type=*/ArrayType::get(Type::getInt8Ty(getContext()), 5),
		/*isConstant=*/true,
		/*Linkage=*/GlobalValue::PrivateLinkage,
				/*Initializer=*/reinterpret_cast<Constant*> (StringLiteralAst(location(), "'%c'").generate()),
				/*Name=*/".fmt_c");
		fmt->setAlignment(1);
	}
}

Codegen::~Codegen()
{
	MALICE_TRACE;

	delete mModule;
	delete mBuilder;
}

Codegen* Codegen::get()
{
	assert (gCodegen != NULL && "No code-generator instance is ever instantiated");
	return gCodegen;
}

LLVMContext& Codegen::getContext()
{
	return mContext;
}

Module* Codegen::finalise(MAliceDriver& driver, Optimisation optimisation)
{
	MALICE_TRACE;

	Module* module = getModule();
	auto_ptr<PassManager> pm = auto_ptr<PassManager>(new PassManager());

	// generate the module
	driver.get()->generate();

	// always include instruction namer pass
	pm->add(createInstructionNamerPass());

	// run our own new optimisation passes
	pm->add(new EntryBlockFinalizer());

	// add optimisation specific optimisations
	int OptLevel = static_cast<int>(optimisation);

	Pass *InliningPass = NULL;
	if (OptLevel)
	{
		unsigned Threshold = 225;
		if (OptLevel > 2)
		{
			Threshold = 281;
		}
		InliningPass = createFunctionInliningPass(Threshold);
	}
	else
	{
		InliningPass = createAlwaysInlinerPass();
	}

	// add standard module passes
	createStandardModulePasses(pm.get(), OptLevel, false, false, true, true, false, InliningPass);

	// add link time optimisations
	createStandardLTOPasses(pm.get(), true, true, true);

	// remove duplicate returns
	pm->add(new MAliceFinalizer());

	// create the function pass manager
	auto_ptr<FunctionPassManager> fpm = auto_ptr<FunctionPassManager>(
			new FunctionPassManager(module));

	createStandardFunctionPasses(fpm.get(), OptLevel);


	// perform optimisation
	cout << "\tEntering optimisation level " << optimisation << endl;


	fpm->doInitialization();
	for (int pass = 10; pass; pass--)
	{
		cout << "\tEntering pass " << pass << endl;
		bool madeChanged = false;

		// optimise all the functions
		for (Module::iterator f = module->begin(); f != module->end(); f++)
		{
			madeChanged = fpm->run(*f) || madeChanged;
		}

		// optimise the module collectively
		if (madeChanged = pm->run(*module) || madeChanged)
		{
			continue;
		}
		else
		{
			break;
		}
	}

	fpm->doFinalization();

	cout << "\tOptimisation level " << optimisation << " completed."<< endl;

	return module;
}

Builder* Codegen::getBuilder()
{
	return mBuilder;
}

Module* Codegen::getModule()
{
	return mModule;
}


string Codegen::getScopeError(const string& name)
{
	MALICE_TRACE;

	stringstream ss;

	int size = mVars.size();
	ss << "Variable '" << name << "' not found on this scope: " << endl;
	for (vector<ScopeType>::iterator j = mVars.begin(); j != mVars.end(); j++, size--)
	{
		ss << setw(4 * size) << "{" << endl;
		for (ScopeType::iterator i = j->begin(); i != j->end(); i++)
		{
			ss << setw(4 * size) << i->first << ", " << endl;
		}
		ss << setw(4 * size) << "}" << endl;
	}

	return ss.str();
}

Function* Codegen::getFunction()
{
	return mFunction;
}

void Codegen::setFunction(Function* f)
{
	mFunction = f;
}

ExpressionAst* Codegen::getDefaultValue(const string& type)
{
	MALICE_TRACE;

	// default value
	if (type == "number")
	{
		return new IntegerLiteralAst(location(), 0);
	}
	else if (type == "letter")
	{
		return new CharacterLiteralAst(location(), '\0');
	}
	else if (type == "sentence")
	{
		return new StringLiteralAst(location(), "null");
	}
	else
	{
		cerr << "Warning: could not deduce the type of variable " << type << ". Expect subsequent failures!"<< endl;
		return NULL;
	}
}

Value* Codegen::getVar(const string& name)
{
	MALICE_TRACE;

	for (vector<ScopeType>::reverse_iterator i = mVars.rbegin(); i != mVars.rend(); i++)
	{
		if (Value* value = (*i) [name])
			return value;
	}

	if (0 == name.find("size:"))
	{
		return NULL;
	}
	else
	{
		throw runtime_error(getScopeError(name));
	}
}

void Codegen::pushVar(const string& name, Value* value, Value* size)
{
	MALICE_TRACE;

	ScopeType& top = mVars.back();

	// ensure we are not redefining
	if (top[name] && top[name] != value)
	{
		throw runtime_error(name + " cannot be redefined!\n" + getScopeError(name));
	}

	top[name] = value;
	if (size)
	{
		top["size:" + name] = size;
	}
}

void Codegen::pushScope()
{
	MALICE_TRACE;

	mVars.push_back(ScopeType());
}

void Codegen::popScope()
{
	MALICE_TRACE;

	// TODO the scope is exiting, we may want to add destructors
	// and invoke them at scope exit
	mVars.pop_back();
}

}
